Assertions are statements that check whether certain conditions are true. They are used to validate that the actual results of a code snippet match
the expected outcomes. By using assertions, developers can ensure that their code behaves as intended and identify potential bugs or issues early in
the development process.
When the unit test is executed, the assertions are evaluated. If all the assertions in the test pass, it means the unit is functioning correctly
for that specific set of inputs. If any of the assertions fail, it indicates that there is a problem with the unit’s implementation, and the test case
helps identify the issue.
It is not good enough to test if an exception is raised, without checking which exception it is. Such tests will not be able to differentiate the
expected exception from an unexpected one.
This rule raises an issue in the following cases:
- When an asynchronous Mocha test calls the
done()
callback, without parameters, in a catch
block, and there is no
reference to the caught exception in this block. Either the error should be passed to done()
or the exception should be checked
further.
- When Chai assertions are used to test if a function throws any exception, or an exception of type
Error
without checking the
message.
- When Chai assertions are used to test if a function does not throw an exception of type
Error
without checking the message.
This rule doesn’t raise an issue when an assertion is negated. In such a case, the exception doesn’t need to be specific.
const expect = require("chai").expect;
const fs = require("fs");
describe("exceptions are not tested properly", function() {
const funcThrows = function () { throw new TypeError('What is this type?'); };
const funcNoThrow = function () { /*noop*/ };
it("forgot to pass the error to 'done()'", function(done) {
fs.readFile("/etc/zshrc", 'utf8', function(err, data) {
try {
expect(data).to.match(/some expected string/);
} catch (e) {
done(); // Noncompliant: either the exception should be passed to done(e), or the exception should be tested further.
}
});
});
it("does not 'expect' a specific exception", function() {
expect(funcThrows).to.throw(); // Noncompliant: the exception should be tested.
expect(funcThrows).to.throw(Error); // Noncompliant: the exception should be tested further.
});
});
Tests should instead validate the exception message and/or type. By checking for the specific exception that is expected to be thrown, the test case can verify that the code is behaving correctly and handling exceptions as expected. This helps to improve the overall quality and reliability of the code.
const expect = require("chai").expect;
const { AssertionError } = require('chai');
const fs = require("fs");
describe("exceptions are tested properly", function() {
const funcThrows = function () { throw new TypeError('What is this type?'); };
const funcNoThrow = function () { /*noop*/ };
it("did not forget to pass the error to 'done()'", function(done) {
fs.readFile("/etc/zshrc", 'utf8', function(err, data) {
try {
expect(data).to.match(/some expected string/);
} catch (e) {
expect(e).to.be.an.instanceof(AssertionError);
done();
}
});
});
it("does 'expect' a specific exception", function() {
expect(funcThrows).to.throw(TypeError);
expect(funcNoThrow).to.not.throw(Error, /My error message/);
});
});